/*
 * Copyright 2007-2008 Andrew O'Malley
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package ws.quokka.plugin.help;

import org.apache.tools.ant.taskdefs.UpToDate;
import org.apache.tools.ant.util.FileUtils;

import org.w3c.dom.Node;

import ws.quokka.core.repo_spi.RepoArtifact;
import ws.quokka.core.util.URLs;
import ws.quokka.core.util.xml.Document;
import ws.quokka.core.util.xml.Element;

import java.io.File;

import java.net.URL;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;


/**
 *
 */
public abstract class AbstractHelpGenerator implements HelpGenerator {
    //~ Instance fields ------------------------------------------------------------------------------------------------

    protected HelpPlugin plugin;
    protected Document metaDocument;
    protected Document helpDocument;
    protected RepoArtifact artifact;
    protected Element dlEl;
    protected boolean summary;

    //~ Methods --------------------------------------------------------------------------------------------------------

    public void setSummary(boolean summary) {
        this.summary = summary;
    }

    public void setArtifact(RepoArtifact artifact) {
        this.artifact = artifact;
    }

    public void setPlugin(HelpPlugin plugin) {
        this.plugin = plugin;
    }

    public void initialise() {
        URL helpURL = getHelpURL();

        if (helpURL != null) {
            helpDocument = Document.parse(helpURL, new Document.NullEntityResolver());
        }

        URL metadataFile = getMetadataURL();

        if (metadataFile != null) {
            metaDocument = Document.parse(metadataFile, new Document.NullEntityResolver());
        }

        dlEl = null;
    }

    protected String getTitle() {
        String title = "Module";

        if (helpDocument != null) {
            Element h1El = helpDocument.getRoot().getChild("body").getChild("h1");
            title = h1El.getText();
        }

        return title;
    }

    protected URL getHelpURL() {
        if (summary) {
            return URLs.toURL(artifact.getLocalCopy(), plugin.getHelpPath(artifact.getId()));
        } else {
            File file = plugin.properties().getFile("helpFile", null);

            return ((file == null) || !file.exists()) ? null : URLs.toURL(file);
        }
    }

    protected abstract URL getMetadataURL();

    public boolean isUpToDate(File out) {
        UpToDate upToDate = (UpToDate)plugin.utils().init(new UpToDate(), "uptodate");
        upToDate.setTargetFile(out);

        List urls = new ArrayList();
        urls.add(getHelpURL());
        urls.add(getMetadataURL());

        for (Iterator i = urls.iterator(); i.hasNext();) {
            URL url = (URL)i.next();
            File file = (url == null) ? null : new File(FileUtils.getFileUtils().fromURI(url.toString()));

            if ((url == null) || !file.exists()) {
                i.remove();
            } else {
                upToDate.addSrcfiles(plugin.utils().toFileSet(file));
            }
        }

        return (urls.size() == 0) || upToDate.eval();
    }

    public void addContent(Document outDoc) {
        // Get the title from the help helpFile
        String title = getTitle();
        String versionText = " (" + artifact.getId().getGroup() + ":" + artifact.getId().getVersion() + ")";

        // Add the title to the meta data and as a h1
        Element titleEl = outDoc.getRoot().getChild("head").getChild("title");
        titleEl.addText(title + versionText);

        Element outBodyEl = outDoc.getRoot().getChild("body");
        outBodyEl.addChild("h1").addText(title).addChild("span").setAttribute("class", "version").addText(versionText);

        String description = getDescription();

        if (description != null) {
            outBodyEl.addChild("p").addText(description);
        }

        copyContent(outDoc, outBodyEl, "content-before");

        addContent(outDoc, outBodyEl);

        copyContent(outDoc, outBodyEl, "content-after");
    }

    /**
     * By default returns the first paragraph of the help document
     */
    protected String getDescription() {
        if (helpDocument != null) {
            List pEls = helpDocument.getRoot().getChild("body").getChildren("p");

            return (pEls.size() == 0) ? null : ((Element)pEls.iterator().next()).getText();
        }

        return null;
    }

    /**
     * Override this to add specialised content
     */
    protected void addContent(Document out, Element outBodyEl) {
    }

    protected void copyContent(Document out, Element outBodyEl, String id) {
        // Copy an extra content from the help helpFile to the output untouched
        if (helpDocument != null) {
            Element contentEl = helpDocument.getRoot().getChild("body").getChild("div", "id", id);

            if (contentEl != null) {
                Node copy = out.getDocument().importNode(contentEl.getElement(), true);
                outBodyEl.getElement().appendChild(copy);
            }
        }
    }

    public void addSummary(Element el, boolean first, boolean last) {
        Element refsEl = null;

        if (dlEl == null) {
            dlEl = el.addChild("dl");
        }

        if (first) {
            refsEl = addPluginSummary(dlEl, artifact);
        }

        plugin.addHelpLink(refsEl, artifact, true);

        if (last) {
            //            dlEl.addChild("br");
        }
    }

    private Element addPluginSummary(Element dlEl, RepoArtifact artifact) {
        dlEl.addChild("dt").addChild("b").addText(artifact.getId().getGroup());

        String description = getDescription();
        dlEl.addChild("dd").addText((description == null) ? "" : description);

        return dlEl.addChild("dd");
    }
}
